#nullable enable

using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using ExcelDataReader.Misc;

namespace ExcelDataReader.Core;

/// <summary>
/// Helpers class.
/// </summary>
internal static partial class Helpers
{
    #if !NET8_0_OR_GREATER
    private static readonly Regex EscapeRegexInstance = new("_x([0-9A-F]{4,4})_", RegexOptions.Compiled);
    #endif

    private static readonly char[] SingleByteEncodingHelper = ['a'];

    /// <summary>
    /// Determines whether the encoding is single byte or not.
    /// </summary>
    /// <param name="encoding">The encoding.</param>
    /// <returns>
    ///     <see langword="true"/> if the specified encoding is single byte; otherwise, <see langword="false"/>.
    /// </returns>
    public static bool IsSingleByteEncoding(Encoding encoding)
    {
        return encoding.GetByteCount(SingleByteEncodingHelper) == 1;
    }

    public static string ConvertEscapeChars(string input)
    {
        return EscapeRegex().Replace(input, m => ((char)uint.Parse(m.Groups[1].Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture)).ToString());
    }

    public static object ConvertFromOATime(double value, bool date1904)
    {
        var dateValue = AdjustOADateTime(value, date1904);
        if (IsValidOADateTime(dateValue))
            return DateTimeHelper.FromOADate(dateValue);
        return value;
    }

    public static object ConvertFromOATime(int value, bool date1904)
    {
        var dateValue = AdjustOADateTime(value, date1904);
        if (IsValidOADateTime(dateValue))
            return DateTimeHelper.FromOADate(dateValue);
        return value;
    }

    public static bool StringStartsWith(string value, char start)
    {
#if NETSTANDARD2_1_OR_GREATER || NET8_0_OR_GREATER
        return value.StartsWith(start);
#else
        return value.Length > 0 && value[0] == start;
#endif
    }
    
    /// <summary>
    /// Convert a double from Excel to an OA DateTime double. 
    /// The returned value is normalized to the '1900' date mode and adjusted for the 1900 leap year bug.
    /// </summary>
    private static double AdjustOADateTime(double value, bool date1904)
    {
        if (!date1904)
        {
            // Workaround for 1900 leap year bug in Excel
            if (value is >= 0.0 and < 60.0)
            {
                return value + 1;
            }
        }
        else
        {
            return value + 1462.0;
        }

        return value;
    }

    private static bool IsValidOADateTime(double value)
    {
        return value is > DateTimeHelper.OADateMinAsDouble and < DateTimeHelper.OADateMaxAsDouble;
    }
    
#if NET8_0_OR_GREATER
    [GeneratedRegex("_x([0-9A-F]{4,4})_")]
    private static partial Regex EscapeRegex();
#else
    private static Regex EscapeRegex() => EscapeRegexInstance;
#endif
}